#include "helper.h"
#include "monitor.h"

extern uint32_t instr;
extern char assembly[80];

/* invalid opcode */
make_helper(inv) {

	uint32_t temp;
	temp = instr_fetch(pc, 4);

	uint8_t *p = (void *)&temp;
	printf("invalid opcode(pc = 0x%08x): %02x %02x %02x %02x ...\n\n", 
			pc, p[3], p[2], p[1], p[0]);

	printf("There are two cases which will trigger this unexpected exception:\n\
1. The instruction at pc = 0x%08x is not implemented.\n\
2. Something is implemented incorrectly.\n", pc);
	printf("Find this pc value(0x%08x) in the disassembling result to distinguish which case it is.\n\n", pc);

	assert(0);
}

/* stop temu */
make_helper(temu_trap) {

	printf("\33[1;31mtemu: HIT GOOD TRAP\33[0m at $pc = 0x%08x\n\n", cpu.pc);

	temu_state = END;

}

make_helper(syscall){
	sprintf(assembly,"syscall");

	uint32_t my_signal = ( instr & 0x3ffffc0 )>>5;

	exception(Sys , my_signal);

}

make_helper(breakp){
	temu_state = STOP;

	sprintf(assembly,"breakp");

	uint32_t my_signal = ( instr & 0x3ffffc0 )>>5;

	exception(Bp , my_signal);
}


make_helper(eret){

	uint32_t tmp1 , tmp2;

	tmp1 = instr & 0x18;
	//sel 

	tmp2 = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	//rs , to distinct mfc0 and mtc0


	uint32_t rd = (instr & RD_MASK) >> (SHAMT_SIZE + FUNC_SIZE);

	
	uint32_t rt = (instr & RT_MASK) >> (RD_SIZE + SHAMT_SIZE + FUNC_SIZE);


	
	if ( tmp1 == 0x18 ){
		//make sure the code is eret not sel

		cpu.pc = cpu.epc;
		// EPC -> PC

		cpu.my_status.exl = 0;
		//make exl 0

		sprintf(assembly, "eret");
	}else if (tmp2 == 4){
		//mtc0
		switch (rd)
		{
		case 0b1000:
			cpu.err_vaddr = reg_w(rt);
			break;
		case 0b1100:
			cpu.my_status.sum = reg_w(rt);
			break;
		case 0b1101:
			cpu.my_cause.sum = reg_w(rt);
			break;
		case 0b1110:
			cpu.epc = reg_w(rt);
			break;
		default:
			break;
		}

		sprintf(assembly, "mfc0   %s,   %s,   %d", REG_NAME(rt), REG_NAME(rd), instr & 0b0011);


	}else if (tmp2 == 0){
		//mtc0
		switch (rd)
		{
		case 0b1000:
			reg_w(rt) = cpu.err_vaddr;
			break;
		case 0b1100:
			reg_w(rt) = cpu.my_status.sum;
			break;
		case 0b1101:
			reg_w(rt) = cpu.my_cause.sum ;
			break;
		case 0b1110:
			reg_w(rt) = cpu.epc ;
			break;
		default:
			break;
		}

		sprintf(assembly, "mtc0   %s,   %s,   %d", REG_NAME(rt), REG_NAME(rd), instr & 0b0011);


	}

}

